package com.skyyan.netty.jjf.codec;


import com.skyyan.netty.jjf.protocol.FrameConstants;
import com.skyyan.netty.jjf.protocol.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 生产级Netty编码器，将Message对象转换为符合JJF规范的字节流
 *
 * 帧格式：
 * +--------+--------+--------+--------+--------+--------+--------+--------+
 * | 起始符 |   长度L (1字节)  | 起始符 |  控制域+地址域+AFN+用户数据  | CRC8  | 结束符 |
 * |  68H   |    (小端)       |  68H   |        (L字节)           |       |  16H  |
 * +--------+--------+--------+--------+--------+--------+--------+--------+
 */

public class JjfMessageEncoder extends MessageToByteEncoder<Message> {

    private static final Logger log = LoggerFactory.getLogger(JjfMessageEncoder.class);



    @Override
    protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {
        try {
            int userDataLength = msg.getUserDataLength();
            log.debug("用户数据总长度 = {}", userDataLength);

            if (userDataLength > 255) {
                throw new IllegalArgumentException("用户数据长度超出限制: " + userDataLength);
            }

            // ✅ 1. 记录 CRC 起始位置：从第一个 68H 之前开始
            int crcStartIndex = out.writerIndex();

            // ✅ 2. 写入帧头（全部参与 CRC）
            out.writeByte(FrameConstants.FRAME_START); // 68H
            // 3. 写入长度 L 1个字节
            out.writeByte((byte) userDataLength);       // L = C + A + AFN + UserData 长度

            out.writeByte(FrameConstants.FRAME_START); // 68H

            // ✅ 3. 写入报文体（C + A + AFN + UserData），也参与 CRC
            msg.encodeBody(out);

            // ✅ 4. 此时 writerIndex 指向 CRC 之前，准备计算 CRC
            int crcEndIndex = out.writerIndex();
            int lengthForCrc = crcEndIndex - crcStartIndex;

            // ✅ 5. 验证长度一致性（可选，用于调试）
            if (lengthForCrc != userDataLength + 3) { // +3 = 68 L 68
                log.warn("参与CRC的字节长度({})与预期({})不一致", lengthForCrc, userDataLength + 3);
            }

            // ✅ 6. 提取参与 CRC 的数据并计算
            byte[] dataForCrc = new byte[lengthForCrc];
            out.getBytes(crcStartIndex, dataForCrc); // 读取 [68][L][68][C][A][AFN][UserData]
            byte crc = Crc8Util.calculate(dataForCrc);

            // ✅ 7. 写入 CRC 和帧尾
            out.writeByte(crc);
            out.writeByte(FrameConstants.FRAME_END); // 16H

            // ✅ 8. 输出完整帧 HEX（用于调试）
            int frameStart = out.readerIndex();
            int frameLength = out.readableBytes();
            byte[] frameBytes = new byte[frameLength];
            out.getBytes(frameStart, frameBytes);
            String hexString = bytesToHex(frameBytes);
            log.debug("编码成功: {}, 帧数据(HEX): {}", msg, hexString);

        } catch (Exception e) {
            log.error("编码消息失败: {}", msg.getClass().getSimpleName(), e);
            out.clear(); // 清理状态，避免残留数据
            throw e;
        }
    }
    // ✅ 工具方法：字节数组转16进制字符串
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }
    @Override
    public boolean acceptOutboundMessage(Object msg) throws Exception {
        // 只接受Message类型的对象
        return msg instanceof Message;
    }
}

